Skip to content

Add e2b_id: the Python half of the prefixed-ID format - #1628

Draft
mishushakov wants to merge 1 commit into
prefixed-id-packagesfrom
python-id
Draft

Add e2b_id: the Python half of the prefixed-ID format#1628
mishushakov wants to merge 1 commit into
prefixed-id-packagesfrom
python-id

Conversation

@mishushakov

@mishushakov mishushakov commented Jul 30, 2026

Copy link
Copy Markdown
Member

Stack: 2 of 2, on top of #1627. Review that one first — this PR's diff is the Python package plus the wiring that ties the two halves together.

e2b_id is the same format as @e2b/id, name for name: create_id, encode_id, decode_id, parse_id, is_id, create_uuid, encode_bytes/decode_bytes. Standard library only — no dependencies at all, so an ID codec every service depends on can never be the reason a resolution fails.

Part of SDK-303

Usage

from e2b_id import create_id, decode_id, encode_id, is_id, parse_id

create_id("project")                                          # 'prj_uk75vf2v7iagp2kgn7pfze3car'
encode_id("volume", "019fa519-bfc5-784d-9386-a5d7a93a692a")    # 'vol_uxl2sotjfiagp2kgn7yv4e3e4g'
decode_id("volume", "vol_uxl2sotjfiagp2kgn7yv4e3e4g")          # UUID('019fa519-bfc5-784d-9386-a5d7a93a692a')
parse_id("vol_uxl2sotjfiagp2kgn7yv4e3e4g")                     # ParsedId(kind='volume', uuid=UUID('019fa519-…'))
is_id("project", "vol_uxl2sotjfiagp2kgn7yv4e3e4g")             # False

InvalidIdException carries a reason ("kind" | "prefix" | "length" | "alphabet" | "canonical" | "uuid") and actual_kind, matching the JS side, so callers branch on a field rather than on message prose. It subclasses ValueError, so existing except ValueError guards keep working.

How agreement with @e2b/id is enforced, not asserted

Three independent mechanisms, so a format change on one side alone cannot pass on the other:

  1. The same golden vectors, computed with base64.b32encode rather than captured from either implementation — so they check the code rather than record it.
  2. The same CORPUS_DIGEST: both suites build an identical 1303-value corpus from the same seeded LCG and pin one sha256 over every encoding of it.
  3. Each checks its own codec against an independent reference encoder stated as arithmetic rather than bit shuffling, plus #1627's suite pipes the whole corpus through python3 to verify the six-line b32encode snippet both READMEs advertise.

I also confirmed 300 live-minted IDs round-trip between the built JS bundle and this package across all six kinds.

The one place the surfaces differ

Downstream of JavaScript having no UUID type, this package moves uuid.UUID objects where @e2b/id moves canonical hex strings:

  • create_uuid(), decode_id() and ParsedId.uuid return a uuid.UUID; the JS counterparts return strings, so a round trip is string-to-string in JS but not here.
  • There is no uuid_to_bytes/bytes_to_uuidu.bytes and uuid.UUID(bytes=…) already are those.
  • encode_id() accepts either a uuid.UUID or the hex form.

uuids.py and the README both enumerate this. Everything else — names, argument order, failure modes, wire format — is identical.

Also in this PR

The wiring the split deferred: id_tests.yml grows a Python leg (uv sync --locked, uv build, pytest), lint.yml/typecheck.yml sync the new uv env so the recursive root scripts cover it, the root .gitignore picks up .ruff_cache/, and js-id's README and test docs now name their counterpart.

Not published to PyPI

packages/python-sdk publishes via a postPublish script; this package deliberately has none, because pointing one at a project that does not exist — with a token that may not be scoped for it — would fail pnpm run publish and take the whole monorepo's release down with it. Register the e2b_id project, confirm PYPI_TOKEN covers it, then add the same line python-sdk has.

Verification

102 tests, plus ruff, ty and uv build clean. The ID Package Tests workflow runs both halves on ubuntu-22.04 and windows-latest with no secrets.

🤖 Generated with Claude Code

The same format as @e2b/id, name for name: create_id, encode_id,
decode_id, parse_id, is_id, create_uuid, encode_bytes/decode_bytes.
Standard library only — no dependencies at all, so an ID codec that
every service depends on can never be the reason a resolution fails.

Agreement with the JS half is enforced rather than asserted. Both suites
carry the same golden vectors, computed with base64.b32encode rather
than captured from either implementation; both build the same
1303-value corpus from the same seeded LCG; and both pin the same
CORPUS_DIGEST over it, so a format change on one side alone fails on
both. Each also checks its own codec against an independent reference
encoder stated as arithmetic rather than bit shuffling.

The one place the surfaces differ is downstream of JavaScript having no
UUID type: this package moves uuid.UUID objects where @e2b/id moves
canonical hex strings, so decode_id and ParsedId.uuid return UUIDs and
there is no uuid_to_bytes/bytes_to_uuid — the standard library already
is those. uuids.py and the README both enumerate it.

Also wires the two together: the id_tests workflow grows a Python leg,
lint and typecheck sync the new uv env, and js-id's docs now name their
counterpart.

Not published to PyPI. python-sdk publishes via a postPublish script;
this package deliberately has none, because pointing one at a project
that does not exist with a token that may not be scoped for it would
fail `pnpm run publish` and take the whole monorepo's release down.
Register the project, confirm the token, then add the script.

Co-Authored-By: Claude <noreply@anthropic.com>
@cla-bot cla-bot Bot added the cla-signed label Jul 30, 2026
@cursor

cursor Bot commented Jul 30, 2026

Copy link
Copy Markdown

PR Summary

Low Risk
New isolated codec package and CI wiring; no changes to SDK auth or production services. Wire-format mistakes are guarded by shared corpus digest and existing JS interop tests.

Overview
Introduces packages/python-id (e2b_id): a Python port of the prefixed rotated-base32 ID format with the same API names as @e2b/id (create_id, encode_id, decode_id, parse_id, is_id, create_uuid, byte codec, InvalidIdException). Runtime has no dependencies; dev uses pytest, ruff, and ty via uv.

Cross-language contract: tests/vectors.py mirrors js-id/tests/vectors.ts (goldens, LCG corpus, pinned CORPUS_DIGEST). JS test comments/README now point at the Python package; PyPI publish is not wired (no postPublish) so monorepo release stays safe.

CI: id_tests.yml runs JS + Python build/test in one job (uv replaces bare setup-python); sdk_tests path filter includes python-id; lint and typecheck add uv sync for packages/python-id. .gitignore adds .ruff_cache/.

Reviewed by Cursor Bugbot for commit 6da96f9. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot

changeset-bot Bot commented Jul 30, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 6da96f9

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from 9e96c21. Download artifacts from this workflow run.

JS SDK (e2b@2.36.2-python-id.0):

npm install ./e2b-2.36.2-python-id.0.tgz

CLI (@e2b/cli@2.16.1-python-id.0):

npm install ./e2b-cli-2.16.1-python-id.0.tgz

Python SDK (e2b==2.35.0+python.id):

pip install ./e2b-2.35.0+python.id-py3-none-any.whl

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant